package com.guodefu.unifiedresponse.handler;

import com.guodefu.unifiedresponse.IResult;
import com.guodefu.unifiedresponse.ResultEnum;
import com.guodefu.unifiedresponse.entity.Response;
import com.guodefu.unifiedresponse.exception.BusinessException;
import com.guodefu.unifiedresponse.exception.InheritedException;
import jakarta.validation.ConstraintViolationException;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.NoSuchElementException;
import java.util.Objects;

/**
 * 统一异常处理
 * api全局异常增强处理
 * 默认情况下该类及其子类未被实例化，则会实例化该类
 * 可以添加配置项；unified.exception.enable=false,让此处的实例化失效
 * 若该类及其子类在别处已实例化，则此处不会实例化该类
 * 暂未考虑国际化问题
 *
 * @author guodefu909
 **/
@Slf4j
@RestControllerAdvice
@ConditionalOnMissingBean(value = GlobalExceptionHandler.class)
@ConditionalOnProperty(prefix = "unified.exception", value = "enable", havingValue = "true", matchIfMissing = true)
public class GlobalExceptionHandler {

    /**
     * 应用名
     */
    @Value("${spring.application.name:''}")
    private String appName;

    /**
     * 其他异常处理
     **/
    @ExceptionHandler(Exception.class)
    public Response<Object> handle(Exception e) {
        log.error("非预期异常", e);
        return Response.fail(ResultEnum.SYSTEM_ERROR, appName, e.getMessage());
    }

    /**
     * Http消息不可读异常。
     * <p>
     * 报错原因包括（不完全的列举）：
     * <p>
     * 1.缺少请求体（RequestBody）异常;
     * <p>
     * 2.无效格式异常。比如：参数为数字，但是前端传递的是字符串且无法解析成数字。
     * <p>
     * 3.Json解析异常（非法Json格式）。传递的数据不是合法的Json格式。比如：key-value对中的value(值)为String类型，却没有用双引号括起来。
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public Response<String> handle(HttpMessageNotReadableException e) {
        log.warn("Http消息不可读异常", e);
        return Response.fail(ResultEnum.HTTP_MESSAGE_NOT_READABLE, appName, e.getMessage());
    }

    /**
     * Http请求方法不支持异常
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public Response<String> handle(HttpRequestMethodNotSupportedException e) {
        log.warn("Http请求方法不支持异常", e);
        return Response.fail(ResultEnum.METHOD_NOT_ALLOWED, appName, e.getMessage());
    }

    /**
     * 缺失必要参数异常
     * 参数被 @RequestParam 修饰，且 required = true，却没有传
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Response<String> handle(MissingServletRequestParameterException e) {
        log.warn("缺失必传参数异常", e);
        return Response.fail(ResultEnum.MISSING_REQUEST_PARAMETER, appName, e.getMessage());
    }

    /**
     * 违反约束参数异常
     * ‘@Validated’ 检验出错
     */
    @ExceptionHandler(ConstraintViolationException.class)
    public Response<String> handle(ConstraintViolationException e) {
        log.warn("违反约束参数异常", e);
        return Response.fail(ResultEnum.CONSTRAINT_VIOLATION_PARAMETER, appName, e.getMessage());
    }

    /**
     * spring 的参数绑定异常处理，如必填参数为空等
     **/
    @ExceptionHandler(BindException.class)
    public Response<String> handle(BindException e) {
        log.warn("参数绑定异常", e);
        BindingResult bindingResult = e.getBindingResult();
        StringBuilder msg = new StringBuilder();
        for (ObjectError error : bindingResult.getAllErrors()) {
            msg.append(", ");
            if (error instanceof FieldError) {
                msg.append(((FieldError) error).getField()).append(": ");
            }
            msg.append(error.getDefaultMessage() == null ? "" : error.getDefaultMessage());
        }
        return Response.fail(ResultEnum.PARAMETER_ERROR, appName, msg.substring(2));
    }

    /**
     * 业务异常处理
     **/
    @ExceptionHandler(value = {BusinessException.class})
    public Response<String> handle(BusinessException e) {
        log.warn("业务异常", e);
        IResult resultCode = e.getResultCode();
        String code = resultCode.getCode();
        String message = resultCode.getMessage();
        if (Objects.nonNull(e.getMessage())) {
            // 当e.getMessage()不为null时，表明resultCode.getMessage()为消息模板，e.getMessage()才是最终消息
            message = e.getMessage();
        }
        String detail = e.getDetail();
        return Response.fail(code, message, appName, detail);
    }

    /**
     * 找不到元素异常
     **/
    @ExceptionHandler(value = {NoSuchElementException.class})
    public Response<String> handle(NoSuchElementException e) {
        log.warn("找不到元素异常", e);
        IResult resultCode = ResultEnum.NO_SUCH_ELEMENT;
        return Response.fail(resultCode, appName, e.getMessage());
    }

    /**
     * 调用其他微服务异常
     **/
    @ExceptionHandler(value = {InheritedException.class})
    public Response<?> handle(InheritedException e) {
        log.warn("调用其他微服务异常", e);
        return e.getResponse();
    }

}